using MongoDB.Driver;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.Entities.ProductInvolvement;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.ScreeningTool.Synchronization.Handlers
{
    public class ProductInvolvementDataHandler : IHandler<ProductInvolvementData>, IHasClientFacingProduct
    {
        private static readonly IEnumerable<string> _indicatorFieldsToInclude = new[]
        {
            nameof(ProductInvolvementDataIndicator.ClientFacingProducts),
            nameof(ProductInvolvementDataIndicator.IndicatorSymbol),
            nameof(ProductInvolvementDataIndicator.CategorySymbol),
            nameof(ProductInvolvementDataIndicator.CategoryOfInvolvementType),
            nameof(ProductInvolvementDataIndicator.CategoryOfInvolvementDescription),
            nameof(ProductInvolvementDataIndicator.LevelOfInvolvement),
            nameof(ProductInvolvementDataIndicator.HasLevelOfInvolvement),
            nameof(ProductInvolvementDataIndicator.RangeOwnership),
            nameof(ProductInvolvementDataIndicator.HasOwnershipPercent),
            nameof(ProductInvolvementDataIndicator.StandardAnalysis),
            nameof(ProductInvolvementDataIndicator.Score)
        }.Select(x => $"Indicators.{x}").ToList();

        public ClientFacingProduct ClientFacingProduct { get; set; }

        public ProjectionDefinition<ProductInvolvementData> GetProjection(ProjectionDefinitionBuilder<ProductInvolvementData> builder) => builder
            .Include(_indicatorFieldsToInclude);

        public IEnumerable<ExtraElement> GetExtraElements(ProductInvolvementData entity)
        {
            var indicators = entity.Indicators
                .Where(x => x.ClientFacingProducts == null || x.ClientFacingProducts.Contains(ClientFacingProduct))
                .ToList();

            var mostSignificantInvolvements = indicators
                .GroupBy(x => x.IndicatorSymbol)
                .Select(grp => grp.First(g => g.Score == grp.Max(x => x.Score)))
                .ToList();

            return EnumerableExtensions.Concat(
                       GetExtraElements(indicators, false),
                       GetExtraElements(mostSignificantInvolvements, true),
                       GetOverallProductInvolvement(mostSignificantInvolvements));
        }

        public IEnumerable<ExtraElement> GetExtraElements(IEnumerable<ProductInvolvementDataIndicator> involvements, bool isMostSignificant)
            => involvements.SelectMany(involvement => GetExtraElements(involvement, isMostSignificant));

        private static IEnumerable<ExtraElement> GetExtraElements(ProductInvolvementDataIndicator involvement, bool isMostSignificant)
        {
            var indicatorSymbol = involvement.IndicatorSymbol;
            var categorySymbol = isMostSignificant ? indicatorSymbol + "0" : involvement.CategorySymbol;
            var key = $"{indicatorSymbol} {categorySymbol}";

            if (involvement.CategoryOfInvolvementType == CategoryOfInvolvementType.No)
                return new ExtraElementList();

            if (involvement.CategoryOfInvolvementType == CategoryOfInvolvementType.NotResearched)
                return NotResearchedExtraElements(key);

            var categoryAndLevelOfInvolvement = involvement.CategorySymbol + "-" + involvement.LevelOfInvolvement.Id;
            var rangeOwnership = !involvement.HasOwnershipPercent ? null : involvement.RangeOwnership;

            return new ExtraElementList
            {
                { $"{key} Category of Involvement",         involvement.CategoryOfInvolvementDescription},
                { $"{key} Category of Involvement Id",      involvement.CategorySymbol                   },
                { $"{key} Level of Involvement",            involvement.GetLevelOfInvolvement()          },
                { $"{key} Level of Involvement Id",         involvement.LevelOfInvolvement.Id            },
                { $"{key} Category & Level of Involvement", categoryAndLevelOfInvolvement                },
                { $"{key} Score",                           involvement.Score                            },
                { $"{key} Standard Analysis",               involvement.StandardAnalysis                 },
                { $"{key} Range Ownership",                 rangeOwnership                               },
            };
        }

        private static IEnumerable<ExtraElement> NotResearchedExtraElements(string key)
            => new ExtraElementList
            {
                { $"{key} Category of Involvement", CategoryOfInvolvementType.NotResearched },
            };

        public ExtraElementList GetOverallProductInvolvement(IEnumerable<ProductInvolvementDataIndicator> mostSignificantInvolvements)
        {
            var overallProductInvolvement = mostSignificantInvolvements
                .Where(x => x.Score > 0)
                .Select(x => x.IndicatorSymbol)
                .OrderBy(x => x)
                .JoinStrings(";");

            return new ExtraElementList
                    {
                        { "Overall Product Involvement", overallProductInvolvement }
                    };
        }


    }
    public static class PIIndicatorAssessmentxtensions
    {
        public static string GetLevelOfInvolvement(this ProductInvolvementDataIndicator involvement)
        {
            if (!involvement.HasLevelOfInvolvement && !involvement.HasOwnershipPercent)
                return "Not applicable";

            if (!involvement.HasLevelOfInvolvement)
                return null;

            var start = involvement.LevelOfInvolvement.Start;
            var stop = Math.Abs(involvement.LevelOfInvolvement.Stop - 100) > float.Epsilon
                ? involvement.LevelOfInvolvement.Stop - 0.1
                : involvement.LevelOfInvolvement.Stop;

            return $"{start}-{stop}%";
        }
    }
}

